package coin

import (
	"global/libs/structs"
	"global/model"
	"strings"
	"github.com/garyburd/redigo/redis"
	"global/core"
	"github.com/ngaut/log"
	"time"
	"sync"
	"sort"
	"github.com/shopspring/decimal"
	"global/constants"
)

type CoinSort struct {
	Pool 				*redis.Pool
	coins 				map[string]*structs.CoinTicker
	receive 			chan *structs.CoinTicker

	ticker 				*time.Ticker

	mux 				sync.Mutex
	wg 					sync.WaitGroup
}

func NewCoinSort(pool *redis.Pool) *CoinSort {
	return &CoinSort{
		Pool: 			pool,
		coins: 			make(map[string]*structs.CoinTicker),
		receive: 		make(chan *structs.CoinTicker, 100),
		ticker: 		time.NewTicker(2 * time.Second),
	}
}

func (hc *CoinSort) Run() {
	for {
		select {
		case ct := <-hc.receive:
			hc.resetCoin(ct)
		case <-hc.ticker.C:
			hc.sorts()
		}
	}
}

func (hc *CoinSort) sorts() {
	start := time.Now()

	coins := make([]*structs.CoinTicker, 0)

	hc.mux.Lock()
	for _, v := range hc.coins {
		coins = append(coins, v)
	}
	hc.mux.Unlock()

	sortTypes := []string{"mv", "price", "percent"}
	hc.wg.Add(len(sortTypes))
	for _, v := range sortTypes {
		go func(sortBy string) {
			conn := hc.Pool.Get()
			defer conn.Close()
			defer hc.wg.Done()

			sortedCoins := make([]*structs.CoinTicker, len(coins))
			copy(sortedCoins, coins)
			switch sortBy {
			case "mv":
				sortedCoins = hc.sortByMv(sortedCoins)
			case "percent":
				sortedCoins = hc.sortByPercent(sortedCoins)
			case "price":
				sortedCoins = hc.sortByPrice(sortedCoins)
			}

			core.SaveAnalysisCoinOrder(conn, sortBy, sortedCoins, constants.SortTypeDesc)
			core.SaveAnalysisCoinOrder(conn, sortBy, sortedCoins, constants.SortTypeAsc)
		}(v)
	}
	hc.wg.Wait()

	log.Infof("end sort, coin counts: %d, take time: %s", len(coins), time.Since(start))
}

func (hc *CoinSort) sortByPercent(coins []*structs.CoinTicker) []*structs.CoinTicker {
	sort.Slice(coins, func(i, j int) bool {
		imv, _ := decimal.NewFromString(coins[i].Percent)
		jmv, _ := decimal.NewFromString(coins[j].Percent)
		return imv.GreaterThan(jmv)
	})
	return coins
}

func (hc *CoinSort) sortByPrice(coins []*structs.CoinTicker) []*structs.CoinTicker {
	sort.Slice(coins, func(i, j int) bool {
		imv, _ := decimal.NewFromString(coins[i].Close)
		jmv, _ := decimal.NewFromString(coins[j].Close)
		return imv.GreaterThan(jmv)
	})
	return coins
}

// sort by market value
func (hc *CoinSort) sortByMv(coins []*structs.CoinTicker) []*structs.CoinTicker {
	sort.Slice(coins, func(i, j int) bool {
		imv, _ := decimal.NewFromString(coins[i].MarketValue)
		jmv, _ := decimal.NewFromString(coins[j].MarketValue)
		return imv.GreaterThan(jmv)
	})
	return coins
}

func (hc *CoinSort) resetCoin(ct *structs.CoinTicker) {
	if ct == nil || ct.Symbol == "" {
		return
	}
	hc.mux.Lock()
	hc.coins[ct.Symbol] = ct
	hc.mux.Unlock()
}

func (hc *CoinSort) InitCoinTickers(coins map[string]*model.Coin) {
	conn := hc.Pool.Get()
	defer conn.Close()

	for _, coin := range coins {
		symbol := strings.ToLower(coin.Symbol)
		t, err := core.GetCoinTicker(conn, symbol)
		if err != nil {
			log.Fatalf("Error: %s", err)
		}
		if t == nil || t.Symbol == "" { continue }
		hc.coins[symbol] = t
	}
}